Track cursor
Description
-
This is a custom interactive tool that tracks cursor over an alignment.
-
The user needs to select an alignment and hover over it to track cursor.
-
This code only generates data which has shown in above image, user needs to add the form to show the data.
-
The TrackCursor class which extends DgnElementSetTool which handles different events to interact with UI, the TrackCursor class overrides the events here in this tool .
Remarks
-
This sample code is a part of ManagedSDKExample which you get with SDK installation under "examples" section in SDK installation directory.
-
If you encounter any error while using DgnElementSetTool class, make sure to add a reference to Bentley.DgnDisplayNet.dll by selecting Project > Add Reference or change the projects .csproj file to add reference to this dll .
-
The default dll location will be "C:\Program Files\Bentley\OpenRoads Designer CE 10.11\OpenRoadsDesigner\Bentley.DgnDisplayNet.dll"
-
The method OnDataButton() handles the code for alignment selection.
Source Code
//Required References
using System;
using System.Windows.Forms;
using System.Collections.Generic;
using Bentley.DgnPlatformNET;
using Bentley.DgnPlatformNET.Elements;
using Bentley.GeoCoordinatesNET;
using Bentley.GeometryNET;
using Bentley.MstnPlatformNET;
using Bentley.CifNET.SDK.Edit;
using Bentley.CifNET.GeometryModel.SDK;
using Bentley.CifNET.LinearGeometry;
using Bentley.CifNET.Formatting;
namespace ManagedSDKExample.Examples
{
public enum CommandState
{
PickAlignment = 0,
TrackCursor,
}
/// <summary>
/// Track Cursor
/// Tracks cursor dynamically
/// </summary>
/// <author>Joseph.Ashe</author> <date>07/2019</date>
class TrackCursor : DgnElementSetTool
{
#region Members
private BaseGCS ProjectionGCS { get; set; } = null;
private DgnGCS ModelGCS { get; set; } = null;
private ConsensusConnectionEdit Connection { get; set; } = null;
private CommandState State { get; set; } = CommandState.PickAlignment;
private Alignment SelectedAlignment { get; set; } = null;
private bool SkippedAlignment { get; set; } = false;
private TerrainSurface SelectedTerrain { get; set; } = null;
private List<DataPoint> DataPoints { get; set; } = new List<DataPoint>();
#endregion
#region Tool Overrides
protected override void OnPostInstall()
{
this.ModelGCS = DgnGCS.FromModel(Session.Instance.GetActiveDgnModelRef(), true);
if (this.ModelGCS != null)
this.ProjectionGCS = this.ModelGCS;
else
this.ProjectionGCS = this.LaunchBaseSelectorForm("Select a Projection System");
Bentley.DgnPlatformNET.AccuSnap.LocateEnabled = true;
Bentley.DgnPlatformNET.AccuSnap.SnapEnabled = true;
this.Connection = Bentley.CifNET.SDK.Edit.ConsensusConnectionEdit.GetActive();
GeometricModel geomModel = Connection.GetActiveGeometricModel();
if (geomModel != null)
{
SurfaceEntity activeSurface = geomModel.ActiveSurface;
if (activeSurface != null && activeSurface is TerrainSurface)
this.SelectedTerrain = activeSurface as TerrainSurface;
}
this.SetupAndPromptForNextAction();
}
protected override void OnCleanup()
{
if (this.ProjectionGCS != null)
this.ProjectionGCS.Dispose();
if (this.ModelGCS != null)
this.ModelGCS.Dispose();
}
protected override void SetupAndPromptForNextAction()
{
if (this.SelectedAlignment != null || this.SkippedAlignment || this.Connection == null)
this.State = CommandState.TrackCursor;
switch (this.State)
{
case CommandState.PickAlignment:
{
EndDynamics();
SetLocateCriteria();
SetLocateCursor(true);
BeginPickElements();
NotificationManager.OutputPrompt("Select an alignment or reset");
break;
}
case CommandState.TrackCursor:
{
BeginDynamics();
break;
}
}
}
//Selects alignment
protected override bool OnDataButton(Bentley.DgnPlatformNET.DgnButtonEvent ev)
{
switch (this.State)
{
case CommandState.PickAlignment:
{
Bentley.DgnPlatformNET.HitPath hitPath = DoLocate(ev, true, 0);
if (hitPath != null)
{
Element el = hitPath.GetCursorElement();
if (el != null)
{
Alignment al = (el.ParentElement == null) ? Alignment.CreateFromElement(Connection, el) : Alignment.CreateFromElement(Connection, el.ParentElement);
if (al != null)
{
SelectedAlignment = al;
}
}
}
SetupAndPromptForNextAction();
break;
}
case CommandState.TrackCursor:
{
DataPoint p = new DataPoint(ev.Point, this.ProjectionGCS, this.SelectedAlignment, this.SelectedTerrain);
DataPoints.Add(p);
break;
}
}
return false;
}
protected override void OnDynamicFrame(DgnButtonEvent ev)
{
bool isProjectionValid = this.ProjectionGCS != null;
bool isAlignmentValid = this.SelectedAlignment != null;
bool isSurfaceValid = this.SelectedTerrain != null;
List<string> names = new List<string>();
List<string> values = new List<string>();
DataPoint p = new DataPoint(ev.Point, this.ProjectionGCS, this.SelectedAlignment, this.SelectedTerrain);
p.GetStringLists(ref names, ref values, isProjectionValid, isAlignmentValid, isSurfaceValid, false);
this.DrawGraphics(this.SelectedAlignment, ev);
}
private void DrawGraphics(Alignment alignment, DgnButtonEvent uorPoint)
{
if (alignment != null)
{
DgnModel model = Bentley.MstnPlatformNET.Session.Instance.GetActiveDgnModel();
ModelInfo info = model.GetModelInfo();
double UORTOMETER = 1.0 / info.UorPerMeter;
double x = UORTOMETER * uorPoint.Point.X;
double y = UORTOMETER * uorPoint.Point.Y;
double z = UORTOMETER * uorPoint.Point.Z;
if (!model.Is3d)
z = 0;
DPoint3d metricPoint = new DPoint3d(x, y, z);
double metricZ = z;
LinearPoint lp = alignment.LinearGeometry.ProjectPointOnPerpendicular(metricPoint);
if (lp != null)
{
Profile pr = alignment.ActiveProfile;
if (pr != null)
{
LinearPoint pp = pr.ProfileGeometry.GetPointAtX(lp.DistanceAlong);
if (pp != null)
metricZ = pp.Coordinates.Y;
}
/// Draw line from projected point to cursor
RedrawElems redraw = new RedrawElems();
redraw.DrawMode = DgnDrawMode.TempDraw;
redraw.DrawPurpose = DrawPurpose.Dynamics;
redraw.SetDynamicsViewsFromActiveViewSet(uorPoint.Viewport);
List<DPoint3d> points = new List<DPoint3d>();
DPoint3d pjp = new DPoint3d(lp.Coordinates.X / UORTOMETER, lp.Coordinates.Y / UORTOMETER, metricZ / UORTOMETER);
points.Add(pjp);
points.Add(uorPoint.Point);
LineStringElement lse = new LineStringElement(model, null, points.ToArray());
redraw.DoRedraw(lse);
}
}
}
protected override bool OnResetButton(Bentley.DgnPlatformNET.DgnButtonEvent ev)
{
switch (this.State)
{
case CommandState.PickAlignment:
{
this.SkippedAlignment = true;
SetupAndPromptForNextAction();
return false;
}
case CommandState.TrackCursor:
{
if (this.DataPoints.Count > 0)
{
ReportPoints();
this.DataPoints.Clear();
return false;
}
else
{
ExitTool();
return true;
}
}
}
return true;
}
protected override void OnRestartTool()
{
InstallNewInstance();
}
public override StatusInt OnElementModify(Element element)
{
return StatusInt.Error;
}
public static void InstallNewInstance()
{
TrackCursor tool = new TrackCursor();
tool.InstallTool();
}
#endregion
#region Private Methods
private BaseGCS LaunchBaseSelectorForm(string title, BaseGCS namedGCS = null)
{
BaseSelectorForm form = null;
try
{
form = new BaseSelectorForm(title, namedGCS, string.Empty);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error Occured", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
if (form != null)
{
DialogResult res = form.ShowDialog();
if (res == DialogResult.OK)
return form.SelectedGCS;
}
if (namedGCS != null)
return namedGCS;
return null;
}
#endregion
#region ReportPoints
private void ReportPoints()
{
if (this.DataPoints.Count == 0)
return;
DgnModel model = Bentley.MstnPlatformNET.Session.Instance.GetActiveDgnModel();
if (model == null) return;
var sdkCon = Bentley.CifNET.SDK.Edit.ConsensusConnectionEdit.GetActive();
if (sdkCon == null)
return;
GeometricModel geomModel = sdkCon.GetActiveGeometricModel();
if (geomModel == null)
return;
DgnModel dgn = geomModel.DgnModel;
DgnFile file = dgn.GetDgnFile();
Dictionary<string, string> fileProperties = new Dictionary<string, string>();
fileProperties.Add("Project", dgn.ModelName);
fileProperties.Add("File Name", file.GetFileName());
fileProperties.Add("Last Accessed", file.LastSaveTimeUtc.ToString());
bool isProjectionValid = this.ProjectionGCS != null;
bool isAlignmentValid = this.SelectedAlignment != null;
bool isSurfaceValid = this.SelectedTerrain != null;
Dictionary<string, string> selectedProperties = new Dictionary<string, string>();
if (isProjectionValid)
{
selectedProperties.Add("Geographic Projection System", this.ProjectionGCS.Name);
selectedProperties.Add(" ", this.ProjectionGCS.Description);
selectedProperties.Add("Geographic Projection Ellipsoid", this.ProjectionGCS.EllipsoidName);
selectedProperties.Add(" ", this.ProjectionGCS.EllipsoidDescription);
}
if (isAlignmentValid)
{
selectedProperties.Add("Alignment Name", this.SelectedAlignment.Name);
if (SelectedAlignment.FeatureDefinition != null)
selectedProperties.Add("Alignment Feature", this.SelectedAlignment.FeatureDefinition.Name);
selectedProperties.Add("Alignment Length", FormatForDisplay.Distance(this.SelectedAlignment.LinearGeometry.Length, model, 3));
if (SelectedAlignment.ActiveProfile != null)
{
string name = (String.IsNullOrWhiteSpace(this.SelectedAlignment.ActiveProfile.Name) ? "Active" : this.SelectedAlignment.ActiveProfile.Name);
selectedProperties.Add("Profile Name", name);
if (SelectedAlignment.ActiveProfile.FeatureDefinition != null)
selectedProperties.Add("Profile Feature", this.SelectedAlignment.ActiveProfile.FeatureDefinition.Name);
}
}
if (isSurfaceValid)
{
selectedProperties.Add("Surface Name", this.SelectedTerrain.Name);
if (this.SelectedTerrain.FeatureDefinition != null)
selectedProperties.Add("Surface Feature", this.SelectedTerrain.FeatureDefinition.Name);
}
int count = 1;
List<string> data = new List<string>();
foreach (var p in this.DataPoints)
{
List<string> names = new List<string>();
List<string> values = new List<string>();
p.GetStringLists(ref names, ref values, isProjectionValid, isAlignmentValid, isSurfaceValid, true);
string row = count.ToString() + "|";
foreach (var item in values)
row += item + "|";
row.Trim('|');
data.Add(row);
count++;
}
List<string> lables = new List<string>();
List<string> dummy = new List<string>();
lables.Add("Point");
this.DataPoints[0].GetStringLists(ref lables, ref dummy, isProjectionValid, isAlignmentValid, isSurfaceValid, true);
}
#endregion
}
/// <summary>
/// Data Point
/// </summary>
/// <author>Joseph.Ashe</author> <date>07/2019</date>
internal class DataPoint
{
#region Members
public string East { get; set; } = string.Empty;
public string North { get; set; } = string.Empty;
public string Elevation { get; set; } = string.Empty;
public string Latitude { get; set; } = string.Empty;
public string Longitude { get; set; } = string.Empty;
public string Station { get; set; } = string.Empty;
public string Offset { get; set; } = string.Empty;
public string ProfileElevation { get; set; } = string.Empty;
public string ProfileSlope { get; set; } = string.Empty;
public string TerrainElevation { get; set; } = string.Empty;
public string TerrainSlope { get; set; } = string.Empty;
#endregion
#region Constructors
public DataPoint(DPoint3d uorPoint, BaseGCS projection, Alignment alignment, TerrainSurface surface)
{
DgnModel model = Bentley.MstnPlatformNET.Session.Instance.GetActiveDgnModel();
Bentley.DgnPlatformNET.DistanceFormatter distanceFormatter = new DistanceFormatter(model);
Bentley.DgnPlatformNET.AngleFormatter angleFormatter = new AngleFormatter(model);
angleFormatter.AngleMode = AngleMode.DegMinSec;
angleFormatter.AnglePrecision = AnglePrecision.Use5Places;
ModelInfo info = model.GetModelInfo();
double UORTOMETER = 1.0 / info.UorPerMeter;
double x = UORTOMETER * uorPoint.X;
double y = UORTOMETER * uorPoint.Y;
double z = UORTOMETER * uorPoint.Z;
if (!model.Is3d)
z = 0;
DPoint3d metricPoint = new DPoint3d(x, y, z);
this.SetCoordinateStrings(uorPoint, distanceFormatter, model);
this.SetProjectionStrings(projection, metricPoint, angleFormatter);
this.SetAlignmentStrings(alignment, metricPoint);
this.SetTerrainStrings(surface, metricPoint);
}
#endregion
#region Set Strings
private void SetCoordinateStrings(DPoint3d uorPoint, DistanceFormatter f, DgnModel model)
{
this.East = f.ToString(uorPoint.X);
this.North = f.ToString(uorPoint.Y);
if (model.Is3d)
this.Elevation = f.ToString(uorPoint.Z);
}
private void SetProjectionStrings(BaseGCS projection, DPoint3d metricPoint, AngleFormatter formatter)
{
if (projection != null)
{
GeoPoint latlon;
int status = projection.LatLongFromCartesian(out latlon, ref metricPoint);
if (status == 0)
{
string ns = (latlon.Latitude >= 0) ? "N:" : "S:";
this.Latitude = ns + formatter.ToString(Math.Abs(latlon.Latitude));
string ew = (latlon.Longitude >= 0) ? "E:" : "W:";
this.Longitude = ew + formatter.ToString(Math.Abs(latlon.Longitude));
}
}
}
private void SetAlignmentStrings(Alignment alignment, DPoint3d metricPoint)
{
if (alignment != null)
{
DgnModel model = Bentley.MstnPlatformNET.Session.Instance.GetActiveDgnModel();
LinearPoint lp = alignment.LinearGeometry.ProjectPointOnPerpendicular(metricPoint);
if (lp != null)
{
LinearPoint stationPoint = alignment.LinearGeometry.ProjectPointOnPerpendicular(metricPoint);
// Get numerical station
string stationStr = "";
double stationNum = stationPoint.DistanceOnExtension + stationPoint.DistanceAlong;
// Format it
StationingFormatter sformatter = new StationingFormatter(alignment);
StationFormatSettings settings = StationFormatSettings.GetStationFormatSettingsForModel(model);
sformatter.FormatStation(ref stationStr, stationNum, settings);
this.Station = stationStr;
string rl = (lp.Offset > 0) ? " RT" : " LT";
if (Math.Abs(lp.Offset) < 0.000001)
rl = "";
this.Offset = FormatForDisplay.Distance(lp.Offset, model, 3) + rl;
Profile pr = alignment.ActiveProfile;
if (pr != null)
{
LinearPoint pp = pr.ProfileGeometry.GetPointAtX(lp.DistanceAlong);
if (pp != null)
{
this.ProfileElevation = FormatForDisplay.Distance(pp.Coordinates.Y, model, 3);
this.ProfileSlope = FormatForDisplay.Double(pr.ProfileGeometry.GetSlopeAtX(lp.DistanceAlong)) + "%";
}
}
}
}
}
private void SetTerrainStrings(TerrainSurface surface, DPoint3d metricPoint)
{
if (surface != null)
{
Bentley.TerrainModelNET.DTMDrapedPoint dp = surface.DTM?.DrapePoint(metricPoint);
if (dp != null && Math.Abs(dp.Coordinates.Z) > 0.001)
{
DgnModel model = Bentley.MstnPlatformNET.Session.Instance.GetActiveDgnModel();
this.TerrainElevation = FormatForDisplay.Distance(dp.Coordinates.Z, model, 3);
this.TerrainSlope = FormatForDisplay.Double(dp.Slope) + "%";
}
}
}
#endregion
#region Get Strings
private void AddNameValue(string name, string value, ref List<string> names, ref List<string> values, bool always = false)
{
if (!string.IsNullOrWhiteSpace(value) || always)
{
names.Add(name);
values.Add(value);
}
}
public void GetStringLists(ref List<string> names, ref List<string> values, bool isProjectionValid, bool isAlignmentValid, bool isSurfaceValid, bool always = false)
{
this.AddNameValue("X East", this.East, ref names, ref values, always);
this.AddNameValue("Y North", this.North, ref names, ref values, always);
this.AddNameValue("Z Elevation", this.Elevation, ref names, ref values, always);
if (isProjectionValid)
{
this.AddNameValue("Latitude", this.Latitude, ref names, ref values, always);
this.AddNameValue("Longitude", this.Longitude, ref names, ref values, always);
}
if (isAlignmentValid)
{
this.AddNameValue("Station", this.Station, ref names, ref values, always);
this.AddNameValue("Offset", this.Offset, ref names, ref values, always);
this.AddNameValue("Profile Elevation", this.ProfileElevation, ref names, ref values, always);
this.AddNameValue("Profile Slope", this.ProfileSlope, ref names, ref values, always);
}
if (isSurfaceValid)
{
this.AddNameValue("Terrain Elevation", this.TerrainElevation, ref names, ref values, always);
this.AddNameValue("Terrain Slope", this.TerrainSlope, ref names, ref values, always);
}
}
#endregion
}
}